// test_popep++.cpp

#include <popen++/popen.hpp>

#include <boost/test/unit_test.hpp>
#include <boost/test/test_tools.hpp>

#include <boost/algorithm/string.hpp>

#include <boost/iostreams/stream.hpp>
#include <boost/iostreams/copy.hpp>
#include <boost/iostreams/device/back_inserter.hpp>

#include <iostream>
#include <sstream>
#include <fstream>


/**
 * @file Tests
 **/

namespace io = boost::iostreams;

/// \test
void test_simple_popen(void) {
	process::popen child("python -c \"print 'TEST_STRING'\"", process::popen::with_stdout);

    std::string result;
    io::stream<io::back_insert_device<std::string> > bi(result);
    io::copy(child.stdout(), bi);

	BOOST_CHECK_EQUAL(boost::trim_copy(result), "TEST_STRING");
}

/// \test
void test_popen2(void) {
	process::popen child = process::popen2("python");

	child.stdin() << "print 'TEST_STRING'" << std::flush;
	child.close_stdin();

	std::ostringstream out;
	out << child.stdout().rdbuf() << std::flush;

	BOOST_CHECK_EQUAL(boost::trim_copy(out.str()), "TEST_STRING");
}

void test_popen2_flush(void) {
	process::popen child = process::popen2("cat");

	child.stdin() << "TEST_STRING" << std::endl;

	std::string s;
	child.stdout() >> s;
	child.close_stdin();

	BOOST_CHECK_EQUAL(boost::trim_copy(s), "TEST_STRING");
}

/// \test
void test_popen3(void) {
	process::popen child = process::popen3("python");

	child.stdin() << "import sys\n";
	child.stdin() << "print 'TEST_STRING'\n";
	child.stdin() << "print >> sys.stderr, 'ERROR_STRING'" << std::flush;
	child.close_stdin();


	std::ostringstream out1;
	out1 << child.stdout().rdbuf() << std::flush;
	BOOST_CHECK_EQUAL(boost::trim_copy(out1.str()), "TEST_STRING");

	std::ostringstream out2;
	out2 << child.stderr().rdbuf() << std::flush;
	BOOST_CHECK_EQUAL(boost::trim_copy(out2.str()), "ERROR_STRING");

}

/// \test
void test_popen4(void) {
	process::popen child = process::popen4("python");

	child.stdin() << "import sys\n";

	child.stdin() << "print >> sys.stderr, 'ERROR_STRING'\n" << std::flush;
	child.close_stdin();

	std::ostringstream out;
	out << child.stdout().rdbuf() << std::flush;
	BOOST_CHECK_EQUAL(boost::trim_copy(out.str()), "ERROR_STRING");
}

/// \test
void test_transfer(void) {
    std::stringstream input;
    for (int i = 0; i < 1000000; ++i) {
        input << i << std::endl;
    }

    std::stringstream output;

    process::popen child = process::popen2("cat");
    process::transfer(child, input, output);

    int n = 0;
    std::string line;
    while (std::getline(output, line)) ++n;
    BOOST_CHECK_EQUAL(n, 1000000);
}


/// \test
void test_with_conveyer (void) {
    process::popen conveyer = process::popen2("cat | grep Hello");

    for (int i = 0; i < 5; ++i) conveyer.stdin() << "Hello, world" << std::endl;

    conveyer.close_stdin();

    int count = 0;
    std::string tmp;
    while (conveyer.stdout() && std::getline(conveyer.stdout(), tmp)) ++count;

    BOOST_CHECK_EQUAL(5, count);
}


/// \test
void test_duplicate() {
  process::popen p1 = process::popen2("cat");
  std::string s;

  p1.stdin()  << "TEST_STRING_1" << std::endl;
  p1.stdout() >> s;
  BOOST_CHECK_EQUAL(s, "TEST_STRING_1");

  {
    process::popen p2 = p1;
    p2.stdin()  << "TEST_STRING_2" << std::endl;
    p2.stdout() >> s;
    BOOST_CHECK_EQUAL(s, "TEST_STRING_2");
  }

  p1.stdin()  << "TEST_STRING_3" << std::endl;
  p1.stdout() >> s;
  BOOST_CHECK_EQUAL(s, "TEST_STRING_3");
}


boost::unit_test::test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    boost::unit_test::test_suite* test = BOOST_TEST_SUITE( "Master test suite" );

    test->add(BOOST_TEST_CASE(&test_simple_popen));
    test->add(BOOST_TEST_CASE(&test_popen2));
    test->add(BOOST_TEST_CASE(&test_popen2_flush));
    test->add(BOOST_TEST_CASE(&test_popen3));
    test->add(BOOST_TEST_CASE(&test_popen4));
    test->add(BOOST_TEST_CASE(&test_transfer));
    test->add(BOOST_TEST_CASE(&test_with_conveyer));
    test->add(BOOST_TEST_CASE(&test_duplicate));

  return test;
}
